﻿// //  ********************************************************************************
// //  Copyright (C) 2016 Sebastian Kenter. All rights reserved.
// // 
// //  Name    : MultiServerEngine | MasterServer | MasterServer.cs
// //  Created : 2016 - 02 - 16 20:53
// //  Modified: 2016 - 02 - 20 12:33
// //  ********************************************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using ExitGames.Logging;
using MasterServer.Handler.Response;
using NHibernate;
using Photon.SocketServer;
using Photon.SocketServer.ServerToServer;
using ServerFramework.Implementation.Application;
using ServerFramework.Implementation.Client;
using ServerFramework.Implementation.Configuration;
using ServerFramework.Implementation.Database;
using ServerFramework.Implementation.Database.Domain;
using ServerFramework.Implementation.Server;
using ServerFramework.Interfaces.Application;
using ServerFramework.Interfaces.Client;
using ServerFramework.Interfaces.Configuration;
using ServerFramework.Interfaces.Handler;
using ServerFramework.Interfaces.Server;
using TypeDefs;

namespace MasterServer
{
    public class MasterServer : IServerApplication
    {
        private ServerApplication Server { get; }
        private ITelegramHandlerRegistry HandlerRegistry { get; }
        private IServerPeerRegistry<IServerPeer> ServerPeerRegistry { get; }
        private IClientPeerRegistry<IClientPeer> ClientPeerRegistry { get; }
        private IServerConfiguration ServerConfiguration { get; }
        
        public ServerTypes ServerType => ServerTypes.MasterServer;
        public string ServerName { get; }
        public ILogger Logger { get; }

        public MasterServer(ILogger logger, ServerApplication server, ITelegramHandlerRegistry handlerRegistry,
                            IEnumerable<ITelegramHandler<IServerPeer>> serverHandlers,
                            IEnumerable<ITelegramHandler<IClientPeer>> clientHandlers)
        {
            Logger = logger;
            Server = server;
            HandlerRegistry = handlerRegistry;
            ServerPeerRegistry = new ServerPeerRegistry(Logger);
            ClientPeerRegistry = new ClientPeerRegistry(Logger);

            IList<Parameter_T> parameters = null;

            using (ISession session = SessionManager.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    parameters =
                        session.QueryOver<Parameter_T>()
                            .Where(c => c.Parkompgrp == "Server" && c.Pargrp == Server.ApplicationName)
                            .List<Parameter_T>();
                    transaction.Commit();
                }
                session.Close();
            }

            ServerConfiguration = new ServerConfiguration(parameters);
            ServerName = ServerConfiguration.ServerName;

            Logger.InfoFormat(
                "--------------------------------------------------------------------------------------------------------------------");
            Logger.InfoFormat("Starting up new server of type {0}", ServerType);
            Logger.InfoFormat("Assembly {0} - Version {1}", GetType().Name, GetType().Assembly.ImageRuntimeVersion);
            Logger.InfoFormat(
                "--------------------------------------------------------------------------------------------------------------------");

            RegisterHandler(serverHandlers);
            RegisterHandler(clientHandlers);
        }

        public void Setup()
        {
            Logger.InfoFormat("Setup requested for server of type {0}", ServerType);
        }

        public void TearDown()
        {
            Logger.InfoFormat("TearDown requested for server of type {0}", ServerType);

            using (ISession session = SessionManager.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    IList<ServerPeer_T> sp = session.QueryOver<ServerPeer_T>().List<ServerPeer_T>();

                    foreach (var s in sp)
                    {
                        session.Delete(s);
                    }

                    transaction.Commit();
                }

                using (ITransaction transaction = session.BeginTransaction())
                {
                    IList<Handler_T> h = session.QueryOver<Handler_T>().Where(x => x.ServerType == (int) ServerType).List<Handler_T>();

                    foreach (var e in h)
                    {
                        session.Delete(e);
                    }

                    transaction.Commit();
                }

                session.Close();
            }

        }

        public void OnStopRequested()
        {
            Logger.InfoFormat("OnStopRequested requested for server of type {0}", ServerType);

            IDictionary<Guid, IClientPeer> clientPeers = ClientPeerRegistry.GetClientPeers();
            IDictionary<ServerTypes, IServerPeer> serverPeers = ServerPeerRegistry.GetServerPeers();

            foreach (KeyValuePair<Guid, IClientPeer> clientPeer in from clientPeer in clientPeers
                                                                   let peer = clientPeer.Value as PeerBase
                                                                   where peer != null && peer.Connected
                                                                   select clientPeer)
            {
                ClientPeerRegistry.UnregisterClientPeer(clientPeer.Value);
            }

            foreach (KeyValuePair<ServerTypes, IServerPeer> serverPeer in from serverPeer in serverPeers
                                                                          let peer = serverPeer.Value as ServerPeerBase
                                                                          where peer != null && peer.Connected
                                                                          select serverPeer)
            {
                ServerPeerRegistry.UnregisterServerPeer(serverPeer.Key, serverPeer.Value);
            }
        }

        public void OnServerConnectionFailed(int errorCode, string errorMessage, object state)
        {
            Logger.InfoFormat("Server connection failed with code {0} - reason {1}", errorCode, errorMessage);
        }

        private void RegisterHandler(IEnumerable<ITelegramHandler<IServerPeer>> serverHandlers)
        {
            Logger.InfoFormat("Registering server telegram handlers");
            Logger.InfoFormat(
                "--------------------------------------------------------------------------------------------------------------------");
            using (ISession session = SessionManager.OpenSession())
            {
                foreach (ITelegramHandler<IServerPeer> handler in serverHandlers)
                {
                    using (ITransaction transaction = session.BeginTransaction())
                    {
                        var h = new Handler_T
                                {
                                    Clienthandler = 0 ,
                                    Serverhandler = 1,
                                    ServerType = (int) ServerType,
                                    Handlertype = (int) handler.HandlerType,
                                    Operation = handler.Code,
                                    Suboperation = handler.SubCode ?? 0
                                };

                        session.Save(h);

                        transaction.Commit();


                        if (HandlerRegistry.RegisterServerHandler(handler))
                        {
                            Logger.InfoFormat(
                                "Registered handler {0} (type={1}, code={2}, sub-code={3}) with server {4}",
                                handler.GetType().Name, handler.HandlerType, (OperationCodes) handler.Code,
                                (OperationSubCodes)
                                    (handler.SubCode.HasValue
                                        ? Convert.ToInt32(handler.SubCode.Value)
                                        : Convert.ToInt32(null)),
                                ServerType);

                            if (!ServerConfiguration.HandledOperations.ContainsKey(HandlerTypes.ServerHandler))
                            {
                                ServerConfiguration.HandledOperations.Add(HandlerTypes.ServerHandler,
                                    new List<OperationCodes>());
                            }

                            if (
                                !ServerConfiguration.HandledOperations[HandlerTypes.ServerHandler].Contains(
                                    (OperationCodes) handler.Code))
                            {
                                ServerConfiguration.HandledOperations[HandlerTypes.ServerHandler].Add(
                                    (OperationCodes) handler.Code);
                            }
                        }
                        else
                        {
                            Logger.ErrorFormat(
                                "Could not register handler {0} (type={1}, code={2}, sub-code={3}) with server {4}. Handler already exists!",
                                handler.GetType().Name, handler.HandlerType, (OperationCodes) handler.Code,
                                (OperationSubCodes)
                                    (handler.SubCode.HasValue
                                        ? Convert.ToInt32(handler.SubCode.Value)
                                        : Convert.ToInt32(null)),
                                ServerType);
                        }
                    }
                }
                session.Close();
            }
                

            Logger.InfoFormat(
                "--------------------------------------------------------------------------------------------------------------------");
        }

        private void RegisterHandler(IEnumerable<ITelegramHandler<IClientPeer>> clientHandlers)
        {
            Logger.InfoFormat("Registering client telegram handlers");
            Logger.InfoFormat(
                "--------------------------------------------------------------------------------------------------------------------");

            using (ISession session = SessionManager.OpenSession())
            {
                foreach (ITelegramHandler<IClientPeer> handler in clientHandlers)
                {
                    using (ITransaction transaction = session.BeginTransaction())
                    {
                        var h = new Handler_T
                                {
                                    Clienthandler = 1,
                                    Serverhandler = 0,
                                    ServerType = (int) ServerType,
                                    Handlertype = (int) handler.HandlerType,
                                    Operation = handler.Code,
                                    Suboperation = handler.SubCode ?? 0
                                };

                        session.Save(h);

                        transaction.Commit();

                            if (HandlerRegistry.RegisterClientHandler(handler))
                            {
                                Logger.InfoFormat(
                                    "Registered handler {0} (type={1}, code={2}, sub-code={3}) with server {4}",
                                    handler.GetType().Name, handler.HandlerType, (OperationCodes) handler.Code,
                                    (OperationSubCodes)
                                        (handler.SubCode.HasValue
                                            ? Convert.ToInt32(handler.SubCode.Value)
                                            : Convert.ToInt32(null)),
                                    ServerType);

                                if (!ServerConfiguration.HandledOperations.ContainsKey(HandlerTypes.ClientHandler))
                                {
                                    ServerConfiguration.HandledOperations.Add(HandlerTypes.ClientHandler,
                                        new List<OperationCodes>());
                                }

                                if (
                                    !ServerConfiguration.HandledOperations[HandlerTypes.ClientHandler].Contains(
                                        (OperationCodes) handler.Code))
                                {
                                    ServerConfiguration.HandledOperations[HandlerTypes.ClientHandler].Add(
                                        (OperationCodes) handler.Code);
                                }
                            }
                            else
                            {
                                Logger.ErrorFormat(
                                    "Could not register handler {0} (type={1}, code={2}, sub-code={3}) with server {4}. Handler already exists!",
                                    handler.GetType().Name, handler.HandlerType, (OperationCodes) handler.Code,
                                    (OperationSubCodes)
                                        (handler.SubCode.HasValue
                                            ? Convert.ToInt32(handler.SubCode.Value)
                                            : Convert.ToInt32(null)),
                                    ServerType);
                            }
                        }
                    }

                session.Close();
            }

            Logger.InfoFormat(
                "--------------------------------------------------------------------------------------------------------------------");
        }

        public Dictionary<HandlerTypes, List<OperationCodes>> GetHandledOperations()
        {
            return ServerConfiguration.HandledOperations;
        }

        public bool GetIsMasterPeer()
        {
            return ServerConfiguration.IsMasterServer;
        }

        public string GetConfiguration()
        {
            DataContractSerializer dcs = new DataContractSerializer(typeof (ServerConfiguration));
            StringBuilder sb = new StringBuilder();
            using (XmlWriter xw = XmlWriter.Create(sb))
            {
                dcs.WriteObject(xw, ServerConfiguration);
            }
            return sb.ToString();
        }
    }
}
